package com.qf.rbac2202.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.rbac2202.sys.entity.SysMenu;
import com.qf.rbac2202.sys.entity.SysRoleMenu;
import com.qf.rbac2202.sys.entity.SysUserRole;
import com.qf.rbac2202.sys.mapper.SysMenuMapper;
import com.qf.rbac2202.sys.service.ISysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.rbac2202.sys.service.ISysRoleMenuService;
import com.qf.rbac2202.sys.service.ISysUserRoleService;
import com.qf.rbac2202.utils.Constants;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author dong_
 * @since 2022-10-28
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Autowired
    ISysUserRoleService userRoleService;

    @Autowired
    ISysRoleMenuService roleMenuService;

    @Override
    public Set<String> queryAdminPermSet() {

        final List<SysMenu> menuList = list();

        // List<SysMenu>  ---->  List<权限字符串>
        final List<String> permList = menuList.stream()
                .filter(sysMenu -> sysMenu.getPerms() != null)
                .map(sysMenu -> sysMenu.getPerms())
                .collect(Collectors.toList());

        // 处理数据库字段中的 权限字符串之间的逗号
        final HashSet<String> permSet = new HashSet<>();

        for (String pers : permList) {
            if (pers.contains(",")) {
                final String[] split = pers.split(",");
                permSet.addAll(Arrays.asList(split));
            }
        }

        return permSet;
    }

    @Override
    public List<String> queryPermList(Long userId) {
        // 根据用户id查询用户角色id
        final QueryWrapper<SysUserRole> sysUserRoleQueryWrapper = new QueryWrapper<>();

        sysUserRoleQueryWrapper.select("role_id");

        sysUserRoleQueryWrapper.eq("user_id",userId);

        final List<Object> roleIdList = userRoleService.listObjs(sysUserRoleQueryWrapper);

        // 查询 roleIdList 对应的 menuIdList

        final QueryWrapper<SysRoleMenu> sysRoleMenuQueryWrapper = new QueryWrapper<>();
        sysRoleMenuQueryWrapper.select("menu_id");
        sysRoleMenuQueryWrapper.in("role_id",roleIdList);

        final List<Object> menuIdList = roleMenuService.listObjs(sysRoleMenuQueryWrapper);

        // 查询 menuIdList 对应的所有菜单信息 List<权限字符串>

        final QueryWrapper<SysMenu> sysMenuQueryWrapper = new QueryWrapper<>();
        sysMenuQueryWrapper.select("perms");
        sysMenuQueryWrapper.in("menu_id",menuIdList);

        final List<Object> permsList = listObjs(sysMenuQueryWrapper);

        final List<String> permsListNoRepeate = permsList.stream()
                .map(o -> o.toString())
                .flatMap( s -> Arrays.stream(s.split(",") ))
                .distinct()
                .collect(Collectors.toList());
        return permsListNoRepeate;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<SysMenu> getUserMenuList(Long userId) {

        //1. 如果登录用户是管理员的话， 那么可以看到所有菜单
        if (userId.equals(Constants.SUPER_ADMIN_ID)){
            final List<SysMenu> allMenulist = this.list();
            List<SysMenu> menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID,allMenulist);
            return menuList;
        }else {
            //2. 如果非管理员用户，只能看到自己拥有角色所对应的菜单

            List<SysMenu> allMenuList = queryAllMenuByUserId(userId);
            List<SysMenu> menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID,allMenuList);
            return menuList;
        }
    }



    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<SysMenu> queryAllMenuByUserId(Long userId) {

        //1 查询当前用户id 对于的所有 角色 id
        final QueryWrapper<SysUserRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("role_id");
        queryWrapper.eq("user_id",userId);

        final List<Object> roleIdList = userRoleService.listObjs(queryWrapper);
        //2 查询 sys_role_menu 表，把 roleIdList 对于的所有 menu_id 查出来
        final QueryWrapper<SysRoleMenu> sysRoleMenuQueryWrapper = new QueryWrapper<>();
        sysRoleMenuQueryWrapper.select("menu_id");
        sysRoleMenuQueryWrapper.in("role_id",roleIdList);

        final List<Object> menuIdList = roleMenuService.listObjs(sysRoleMenuQueryWrapper);
        //3 查询 menuIdList 对于的 所有 菜单
        final QueryWrapper<SysMenu> sysMenuQueryWrapper = new QueryWrapper<>();
        sysMenuQueryWrapper.in("menu_id",menuIdList);
        return this.list(sysMenuQueryWrapper);

    }

    /**
     * 把当前用户的所有菜单，组织成 层级结构
     * @param topMenuParentId
     * @param allMenulist
     * @return
     */
    private List<SysMenu> organizeMenu(Long topMenuParentId, List<SysMenu> allMenulist) {

        //1. 从全部菜单中获取 topMenuParentId 对应的 菜单 list1
        final List<SysMenu> list1 = allMenulist
                .stream()
                .filter(sysMenu -> sysMenu.getParentId().equals(topMenuParentId))
                .collect(Collectors.toList());
        //2. 子过程（填充菜单populateChildMenu,设计这个方法方便实现递归调用
        populateChildMenu(list1,allMenulist);

        return list1;
    }


    private void populateChildMenu(List<SysMenu> list1, List<SysMenu> allMenulist) {

        //1. 遍历 list1 ，对于每一个菜单，查找该菜单下是否有子菜单，
        // 如果有，继续递归调用populateChildMenu ，为所有子菜单继续 填充下级菜单

        for (SysMenu sysMenu : list1) {

            //查找当前菜单的下级菜单，返回 childList
            List<SysMenu> childList = findChildMenu(sysMenu,allMenulist);

            if(childList!=null&&childList.size()>0){  // 有子菜单

                // 为所有子菜单填充下级菜单
                populateChildMenu(childList,allMenulist);

                // 挂载子菜单到当前菜单
                sysMenu.setList(childList);
            }

        }

    }


    /**
     * 查找当前菜单的子菜单
     * @param sysMenu  当前菜单
     * @param allMenulist  子菜单
     * @return
     */
    private List<SysMenu> findChildMenu(SysMenu sysMenu, List<SysMenu> allMenulist) {

        final List<SysMenu> childList = allMenulist.stream()
                // 过滤子菜单
                .filter(menu -> sysMenu.getMenuId().equals(menu.getParentId()))
                .collect(Collectors.toList());

        return childList;
    }


}
